package com.ipan.kits.io;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ipan.kits.base.Platforms;

/**
 * 系统调用工具
 * 
 * @author iPan
 * @version 2018-07-18
 */
public class CmdUtil {
	
	public static final String CMD_ENCODE = "GBK";
	protected static Logger LOG = LoggerFactory.getLogger(CmdUtil.class);
	
	private CmdUtil() {}

	//-- 简单的系统调用 --//
	public static String simpleExec(String command) {
		return simpleExec(command, null, null, true, CMD_ENCODE, 0);
	}
	public static String simpleExec(String command, boolean syc) {
		return simpleExec(command, null, null, syc, CMD_ENCODE, 0);
	}
	public static String simpleExec(String command, boolean syc, String encode) {
		return simpleExec(command, null, null, syc, encode, 0);
	}
	/**
	 * 简单的系统调用
	 * @param syc true是同步执行的，主线程会阻塞，有返回值；false是异步执行的，主线程不会阻塞，无返回值；
	 */
	public static String simpleExec(String command, String[] envp, File dir, boolean syc, String encode, long msec) { // exec("cmd /c dir")
		StringBuilder buf = new StringBuilder();
		BufferedReader in = null;
		BufferedReader err = null;
		try {
			Process p = Runtime.getRuntime().exec(command, envp, dir);
			if (syc) {
				// 命令返回
				in = new BufferedReader(new InputStreamReader(p.getInputStream(), encode));
				String line = in.readLine();
				while (line != null) {
					if (buf.length() > 0) {
						buf.append(Platforms.LINE_SEPARATOR);
					}
					buf.append(line);
					line = in.readLine();
				}
				// 错误输出
				err = new BufferedReader(new InputStreamReader(p.getErrorStream(), CMD_ENCODE));
				line = err.readLine();
				while (line != null) {
					LOG.error(line);
					line = err.readLine();
				}
			} else {
				int exitCode = -1;
				try {
					if (msec <= 0) {
						exitCode = p.waitFor();
					} else if (msec  > 0) {
						if (p.waitFor(msec, TimeUnit.MILLISECONDS)) {
							exitCode = p.exitValue();
						}
					}
					buf.append("exitCode=").append(exitCode);
				} catch (InterruptedException e) {
					e.printStackTrace();
					buf.append("exitCode=").append(exitCode);
				}
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
				}
			}
			if (err != null) {
				try {
					err.close();
				} catch (IOException e) {
				}
			}
		}
		return buf.toString();
	}
	
	//-- 标准的系统调用 --//
	public static String exec(List<String> command) {
		return exec(command, null, true, CMD_ENCODE);
	}
	public static String exec(List<String> command, boolean syc) {
		return exec(command, null, syc, CMD_ENCODE);
	}
	public static String exec(List<String> command, boolean syc, String encode) {
		return exec(command, null, syc, encode);
	}
	/**
	 * 标准的系统调用
	 * 通常，我们需要当前进程等待子进程执行完后再继续往下执行，可以将syc=true，这样当前进程就会等待，直到子进程执行完毕后才会往下执行；
	 * 有两种方案：
	 * 1 在主线程中读取子进程的输出流，因为标准IO流是阻塞的，所以，主线程会阻塞等待子进程的输出流；这样做的好处是不需要再开启一个线程，但是
	 *  子进程的执行结果需要子进程完全执行完毕后才会输出；对于不要求实时显示子进程的执行结果的需求，可以使用这种；
	 * 2 启动另一个线程读取子进程的输出流并调用redirectErrorStream(true)将输出流与错误流合并，然后在主线程上调用waitFor()来等待子进程的执行；
	 * 	当waitFor()返回结果时，子进程执行完毕；这样做的好处是，可以实时输出子进程的输出结果；
	 */
	public static String exec(List<String> command, File dir, boolean syc, String encode) {
		StringBuilder buf = new StringBuilder();
		BufferedReader in = null;
		Process p = null;
		try {
			// redirectErrorStream，必须合并，否则在有些进程中执行会出现无限制的等待；
			p = new ProcessBuilder(command).directory(dir).redirectErrorStream(true).start(); // 合并in和err
			if (syc) {
				// 命令返回
				in = new BufferedReader(new InputStreamReader(p.getInputStream(), encode));
				String line = in.readLine();
				while (line != null) {
					if (buf.length() > 0) {
						buf.append(Platforms.LINE_SEPARATOR);
					}
					buf.append(line);
					line = in.readLine();
				}
			}
			
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
				}
			}
			if (p != null) {
				p.destroy();
			}
		}
		return buf.toString();
	}
	
	//-- 实时显示执行结果的系统调用 --//
	/**
	 * 实时显示执行结果的系统调用
	 */
	public static void realExec(List<String> command) {
		realExec(command, null, System.out, CMD_ENCODE);
	}
	public static void realExec(List<String> command, final OutputStream out) {
		realExec(command, null, out, CMD_ENCODE);
	}
	public static void realExec(List<String> command, final OutputStream out, String encode) {
		realExec(command, null, out, encode);
	}
	public static void realExec(List<String> command, File dir, final OutputStream out, String encode) {
		try {
			final Process p = new ProcessBuilder().command(command).directory(dir).redirectErrorStream(true).start(); // 合并in和err
			
			// 实时输出子进程执行情况
			new Thread(new Runnable() {
				@Override
				public void run() {
					BufferedReader in = null;
					try {
						in = new BufferedReader(new InputStreamReader(p.getInputStream(), encode));
						String line = in.readLine();
						while (line != null) {
							out.write((line + Platforms.LINE_SEPARATOR).getBytes());
							line = in.readLine();
						}
					} catch (Exception e) {
						LOG.error("thread error", e);
					} finally {
						if (in != null) {
							try {
								in.close();
							} catch (IOException e) {
							}
						}
					}
				}
			}).start();
			
			// 主线程等待子进程执行完
			int code = p.waitFor();
			LOG.info("return waitFor code: " + code);
			p.destroy();
		} catch (Exception e) {
			LOG.error("exec error", e);
		}
	}
	
	public static void main(String[] args) throws Exception {
		long startTime = System.currentTimeMillis();
		System.out.println("begin main");
//		String result = exec("cmd /c copy /y d:\\1.zip d:\\2.zip");
//		String result = exec("cmd /c copy /y d:\\1.zip d:\\2.zip", false);
		
//		List<String> cmdList = new ArrayList<String>();
//		cmdList.add("cmd");
//		cmdList.add("/c");
//		cmdList.add("copy");
//		cmdList.add("/y");
//		cmdList.add("d:\\1.zip");
//		cmdList.add("d:\\2.zip");
//		String result = exec(cmdList);
//		String result = exec(cmdList, false);
		
//		List<String> cmdList = new ArrayList<String>();
//		cmdList.add("ping");
//		cmdList.add("-n");
//		cmdList.add("5");
//		cmdList.add("127.0.0.1");
//		String result = exec(cmdList);
//		String result = exec("ping -n 5 127.0.0.1", false);
		
//		String result = exec("d:/mytools/ffmpeg.exe -i d:/u0/1531554855549.mp4 -y d:/u0/1531554855549.mov");
		List<String> convert = new ArrayList<String>();
		convert.add("d:/mytools/ffmpeg.exe"); // 添加转换工具路径
		convert.add("-i"); // 添加参数＂-i＂，该参数指定要转换的文件
		convert.add("d:/u0/1531554855549.mp4"); // 添加要转换格式的视频文件的路径
		convert.add("-y"); // 添加参数＂-y＂，该参数指定将覆盖已存在的文件
		convert.add("d:/u0/1531554855549.mov");
		realExec(convert, System.out);
//		String result = exec(convert, true);
//		System.out.println(result);
		long msTime = System.currentTimeMillis() - startTime;
		System.out.println("end main: " + msTime + "ms");
	}
}
